package tcp2;

import java.io.IOException;

import java.io.InputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.concurrent.ConcurrentHashMap;

public class Server {
    public static void main(String[] args) {
        int port = 65432;
        Server server = new Server(port);
        server.start();
    }

    private int port;

    public Server(int port) {
        this.port = port;
    }

    private volatile boolean running = false;
    private Thread connWatchDog;

    public void start() {
        if (running) return;
        running = true;
        connWatchDog = new Thread(new ConnWatchDog());
        connWatchDog.start();
    }

    class ConnWatchDog implements Runnable {
        public void run() {
            try {
                ServerSocket ss = new ServerSocket(port, 5);  //开一个server socket，等待人来连接
                while (running) {
                    System.out.println("服务端=等待人来连接");
                    Socket s = ss.accept(); //一旦被人连接上会执行到这行，没收到连接之前在这阻塞
                    System.out.println("有一个连接上了，但不知道是谁，开个线程去处理，获取心跳信息，判断是谁传来的心跳信息");
                    new Thread(new SocketAction(s)).start();  //开个新线程去处理本次连接，然后去监听新的连接。start就会自动执行被重写的run方法
                }
            } catch (IOException e) {
                e.printStackTrace();
                Server.this.stop();
            }
        }
    }

    class SocketAction implements Runnable {
        Socket s;
        boolean run = true;
        long lastReceiveTime = System.currentTimeMillis();

        public SocketAction(Socket s) {
            this.s = s;
        }

        //start就会自动执行被重写的run方法
        public void run() {
            while (running && run) {
                if (System.currentTimeMillis() - lastReceiveTime > receiveTimeDelay) {
                    overThis(); //如果最后一次收到心跳的时间到现在的时长大于设置的允许延迟时间，则断开连接  Socket s close
                } else {
                    //否则每隔10MS去获取一下从client发过来的心跳信息，并更新最后一次收到心跳的时间
                    //然后根据client发过来的心跳信息，构建返回给client的信息，告诉client我是server收到了你的心跳信息。
                    //其实这里应该先从心跳表中判断这次传来的是谁的心跳，然后更新该人的心跳时间。本例只有一个client，所以不用管
                    try {
                        InputStream in = s.getInputStream();
                        if (in.available() > 0) {
                            ObjectInputStream ois = new ObjectInputStream(in);
                            Object obj = ois.readObject();
                            lastReceiveTime = System.currentTimeMillis();
                            System.out.println("接收：\t" + obj);

                            ObjectAction oa = (ObjectAction) actionMapping.get(obj.getClass());
                            oa = oa == null ? new DefaultObjectAction() : oa;
                            Object out = oa.doAction(obj, Server.this);
                            if (out != null) {
                                ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
                                oos.writeObject(out);
                                oos.flush();
                            }
                        } else {
                            //睡10MS，再去接收新的心跳信息
                            System.out.println("睡10MS，再去接收新的心跳信息");
                            Thread.sleep(1000);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        overThis();
                    }
                }
            }
        }

        private void overThis() {
            if (run) run = false;
            if (s != null) {
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("关闭：" + s.getRemoteSocketAddress());
        }
    }

    /**
     * 要处理客户端发来的对象，并返回一个对象，可实现该接口。
     */
    public interface ObjectAction {
        Object doAction(Object rev, Server server);
    }

    public static final class DefaultObjectAction implements ObjectAction {
        public Object doAction(Object rev, Server server) {
            System.out.println("处理并返回：" + rev);
            return rev;
        }
    }


    private long receiveTimeDelay = 3000;

    private ConcurrentHashMap actionMapping = new ConcurrentHashMap();


    //    @SuppressWarnings("deprecation")
    public void stop() {
        if (running) running = false;
        if (connWatchDog != null) connWatchDog.stop();
    }

    public void addActionMap(Class cls, ObjectAction action) {
        actionMapping.put(cls, action);
    }
}

